home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / OWLSRC.PAK / PROPSHT.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  58.9 KB  |  2,319 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // Copyright (c) 1993, 1997 by Borland International, All Rights Reserved
  4. //
  5. //$Revision:   10.34  $
  6. //
  7. // Implementation of TPropertyPage and TPropertySheet classes
  8. //----------------------------------------------------------------------------
  9. #include <owl/pch.h>
  10. #if !defined(OWL_COMMCTRL_H)
  11. # include <owl/commctrl.h>
  12. #endif
  13. #if !defined(OWL_PROPSHT_H)
  14. # include <owl/propsht.h>
  15. #endif
  16. #if !defined(OWL_TABCTRL_H)
  17. # include <owl/tabctrl.h>
  18. #endif
  19. #if !defined(OWL_RESOURCE_H)
  20. # include <owl/resource.h>
  21. #endif
  22. #if !defined(OWL_PROPSHT_RH)
  23. # include <owl/propsht.rh>
  24. #endif
  25. #if !defined(WINSYS_UIMETRIC_H)
  26. # include <winsys/uimetric.h>
  27. #endif
  28.  
  29. OWL_DIAGINFO;
  30.  
  31. //----------------------------------------------------------------------------
  32. // TPropertySheet
  33. //----------------------------------------------------------------------------
  34.                 
  35. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  36.  
  37. // The caption of the PropertySheet buttons are loaded from a resource (to
  38. // facilitate localization). However, we have default text.
  39. //
  40. static struct TSheetBtnInfo {
  41.   int   id;         // Identifier of button
  42.   int   strId;      // Identifier of string resource of caption
  43.   char* defStr;     // Pointer to default when resource is not found
  44. } defBtn[]= { 
  45.   {ID_APPLY,  IDS_APPLY,  "&Apply"},
  46.   {IDCANCEL,  IDS_CANCEL, "Cancel"},
  47.   {IDOK,      IDS_OK,     "OK"}
  48. }
  49. ,hlpBtn = {IDHELP,    IDS_HELP,   "Help"};
  50.  
  51. const int  defBtnSz  = sizeof(defBtn)/sizeof(defBtn[0]);
  52. const char CloseStr[]= "Close";
  53. #endif
  54.  
  55. //
  56. // Constructs a propertySheet object
  57. //
  58. TPropertySheet::TPropertySheet(TWindow* parent, const char far* title,
  59.                                uint startPage, bool isWizard,
  60.                                uint32 flags, TModule* module)
  61.                : TWindow(parent, title, module), IsWizard(isWizard), 
  62.                  SubClassSheet(false)
  63. {
  64.   // Initialize the structure representing the property sheet
  65.   //
  66.   memset(&HeaderInfo, 0, sizeof(HeaderInfo));
  67.   HeaderInfo.dwSize = sizeof(PROPSHEETHEADER);
  68.   HeaderInfo.dwFlags= flags;
  69.   if (IsWizard)
  70.     HeaderInfo.dwFlags |= PSH_WIZARD;
  71.   HeaderInfo.hInstance = *GetModule();
  72.   HeaderInfo.pszCaption = title;
  73.   HeaderInfo.nStartPage = startPage;
  74.  
  75. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  76.  
  77.   Tab = 0;
  78.   Font = 0;
  79.   ActiveIndex = -1;
  80.   ApplyEnabled = false;
  81.  
  82.   // By default, we'll use the native implementation if it's available
  83.   //
  84.   UseNative = TCommCtrl::IsAvailable();
  85.  
  86.   // ObjectWindows will provide the underlying implementation of
  87.   // PropertySheet/TabControl under environments without the Common 
  88.   // Control Library support
  89.   //
  90.   if (!UseNative) {
  91.     PageRect.SetNull();
  92.  
  93.     // Set the Sheet's window style [Non-native implementation only]
  94.     //
  95.     ModifyStyle(WS_CHILD, WS_POPUP|WS_CAPTION|WS_SYSMENU|
  96.                           WS_VISIBLE|DS_MODALFRAME);
  97.     SetExStyle(WS_EX_DLGMODALFRAME);
  98.   }
  99. #endif
  100. }
  101.  
  102. //
  103. // Clean up resources used by PropertySheet object
  104. //
  105. TPropertySheet::~TPropertySheet()
  106. {
  107. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  108.   delete Font;
  109. #endif  
  110. }
  111.  
  112. //
  113. // Updates the caption of the property sheet
  114. //
  115. void
  116. TPropertySheet::SetCaption(const char far* title)
  117. {
  118. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  119.   //
  120.   // When not using the system's underlying implementation of
  121.   // PropertySheets, TWindow's SetCaption is sufficient.
  122.   //
  123.   if (!UseNative) {
  124.     TWindow::SetCaption(title);
  125.     return;
  126.   }
  127. #endif
  128.  
  129.   // Here we'll be a little flexible and allow a call to SetCaption
  130.   // even before the underlying window element has been created...
  131.   //
  132.   if (GetHandle())
  133.     SetTitle(title, 0);
  134.   else {
  135.     // TWindow's implementation will cache a copy of the string in the
  136.     // data member 'Title'
  137.     //
  138.     TWindow::SetCaption(title);
  139.     HeaderInfo.pszCaption = Title;
  140.   }
  141. }
  142.  
  143. //
  144. // Executes modal propertysheet via call to 'Run' method
  145. //
  146. int
  147. TPropertySheet::Execute()
  148. {
  149. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  150.   //
  151.   // When ObjectWindows is providing the underlying implementation
  152.   // of PropertySheet, we'll simply invoke TWindow's Execute method
  153.   //
  154.   if (!UseNative) {
  155.     return TWindow::Execute();
  156.   }
  157. #endif
  158.  
  159.   // Use CommCtrl via 'Run' helper method
  160.   //
  161.   return Run(true);
  162. }
  163.  
  164. //
  165. // Creates a modeless property sheet.
  166. //
  167. bool
  168. TPropertySheet::Create()
  169. {
  170. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  171.   //
  172.   // When ObjectWindows is providing the underlying implementation
  173.   // of PropertySheet, we'll simply invoke TWindow's Create method
  174.   //
  175.   if (!UseNative)
  176.     return TWindow::Create();
  177. #endif
  178.  
  179.   // Use CommCtrl via 'Run' helper method
  180.   //
  181.   return Run(false) != 0;
  182. }
  183.  
  184. //
  185. // Internal routine use allow each page to register the classes
  186. // of it's children
  187. //
  188. void
  189. TPropertyPage::RegisterPageChildObjects(TPropertyPage* page, void*)
  190. {
  191.   CHECK(page);
  192.   page->RegisterChildObjects();
  193. }
  194.  
  195. //
  196. // Brings up modal/modeless propertysheet by invoking COMMCTRL's API.
  197. //
  198. int
  199. TPropertySheet::Run(bool modal)
  200. {
  201.   // At this point our C++ parent object has (should have!) a valid HWND - 
  202.   // hence update our Header structure
  203.   //
  204.   PRECONDITION(Parent->GetHandle());
  205.   HeaderInfo.hwndParent = Parent->GetHandle();
  206.  
  207.   // We're about to use the Common Control Library's implementation
  208.   // of PropertySheet - Check that it's available
  209.   //
  210.   CHECK(TCommCtrl::IsAvailable());
  211.  
  212.   HPROPSHEETPAGE pHandle[MAXPROPPAGES];
  213.  
  214.   // Have each page give us its handle
  215.   //
  216.   HeaderInfo.nPages = 0;
  217.   HeaderInfo.phpage = pHandle;
  218.   GetPageHandles();
  219.  
  220.   // Have each page register the window classes of it's children
  221.   //
  222.   ForEachPage(TActionPageFunc(TPropertyPage::RegisterPageChildObjects), 0);
  223.  
  224.   // Flag modal vs. modeless sheet
  225.   //
  226.   if (modal) {
  227.     HeaderInfo.dwFlags &= ~PSH_MODELESS;
  228.   }
  229.   else {
  230.     // Subclass the control so that this window will receive
  231.     // button click notifications
  232.     //
  233.     EnableSubclass(true);
  234.     HeaderInfo.dwFlags |= PSH_MODELESS;
  235.   }
  236.  
  237.   // Display property sheet
  238.   //
  239.   return TCommCtrl::Dll()->PropertySheet(&HeaderInfo);
  240. }
  241.  
  242. //
  243. // Internal routine used to add the page handle of a 'TPropertyPage'
  244. // pointer to the array of page handles stored in a PROPSHEETHEADER
  245. // structure.
  246. //
  247. static void
  248. fillPageInfo(TPropertyPage* page, PROPSHEETHEADER* pHeader)
  249. {
  250.   CHECK(page);
  251.   CHECK(pHeader);
  252.   CHECK(pHeader->phpage != 0);
  253.   pHeader->phpage[pHeader->nPages] = page->CreatePropertyPage();
  254.   pHeader->nPages++;
  255. }
  256.  
  257. //
  258. // Have each page of dialog hand over the page handle.
  259. //
  260. bool
  261. TPropertySheet::GetPageHandles()
  262. {
  263. #if defined(__TRACE) || defined(__WARN)
  264.   // Retrieve number of pages in this sheet's child list
  265.   //
  266.   int pageCount = GetPageCount();
  267.  
  268.   // A sheet must have a least one page
  269.   //
  270.   CHECK(pageCount);
  271.   CHECK(pageCount <= MAXPROPPAGES);
  272. #endif
  273.  
  274.   // Have each page hand over it's handle
  275.   //
  276.   ForEachPage(TActionPageFunc(fillPageInfo), &HeaderInfo);
  277.  
  278. #if defined(__TRACE) || defined(__WARN)
  279.   // Double check count
  280.   //
  281.   CHECK(pageCount == HeaderInfo.nPages);
  282. #endif
  283.   return true;
  284. }
  285.  
  286. //
  287. // Applies the specified 'action' function to each TPropertyPage
  288. // child of the Sheet. 
  289. // NOTE: The logic here traverses the TPropertySheet's ChildList. Therefore
  290. //       we will miss any page that does not have an associated TPropertyPage
  291. //       inserted in the sheet's ChildList.
  292. void
  293. TPropertySheet::ForEachPage(TActionPageFunc action, void* paramList)
  294. {
  295.   if (GetLastChild()) {
  296.     TWindow*  curChild;
  297.     TWindow*  nextChild = GetLastChild()->Next();
  298.     TPropertyPage* curPage;
  299.     do {
  300.       curChild = nextChild;
  301.       nextChild = nextChild->Next();
  302.       curPage = TYPESAFE_DOWNCAST(curChild, TPropertyPage);
  303.       if (curPage)
  304.         action(curPage, paramList);
  305.     } while (curChild != GetLastChild() && GetLastChild() != 0);
  306.   }
  307. }
  308.  
  309. //
  310. // Applies the specified 'test' function to each 'TPropertyPage'
  311. // of  the sheet and returns the first page which causes the
  312. // 'test' function to return true. Returns '0' if no page meets
  313. // the condition.
  314. //
  315. TPropertyPage*
  316. TPropertySheet::FirstPageThat(TCondPageFunc test, void* paramList)
  317. {
  318.   if (GetLastChild()) {
  319.     TWindow*  curChild;
  320.     TWindow*  nextChild = GetLastChild()->Next();
  321.     TPropertyPage* curPage;
  322.     do {
  323.       curChild = nextChild;
  324.       nextChild = nextChild->Next();
  325.       curPage = TYPESAFE_DOWNCAST(curChild, TPropertyPage);
  326.       if (curPage) {
  327.         if (test(curPage, paramList))
  328.           return curPage;
  329.       }
  330.     } while (curChild != GetLastChild() && GetLastChild() != 0);
  331.   }
  332.   return 0;
  333. }
  334.  
  335. //
  336. // Internal callback used to count the number of pages within
  337. // a Property Sheet.
  338. //
  339. static void
  340. countPages(TPropertyPage* /*page*/, int* pCount)
  341. {
  342.   (*pCount)++;
  343. }
  344.  
  345. //
  346. // Retrieves the number of pages within a particular sheet
  347. //
  348. int
  349. TPropertySheet::GetPageCount() const
  350. {
  351.   int pageCount = 0;
  352.   CONST_CAST(TPropertySheet*,
  353.              this)->ForEachPage(TActionPageFunc(countPages), &pageCount);
  354.   return pageCount;
  355. }
  356.  
  357. //
  358. // Updates the 'HWINDOW' data member of the PropertySheet Object.
  359. // NOTE: This method is called from the InitHandle method of a
  360. //       page of the sheet.
  361. //
  362. void
  363. TPropertySheet::InitHandle(HWND sheetHandle)
  364. {
  365.   PRECONDITION(GetHandle()==0);
  366.   PRECONDITION(::IsWindow(sheetHandle));
  367.   SetHandle(sheetHandle);
  368.  
  369.   // When using the system's underlying implementation of PropertySheet
  370.   // should we subclass the Sheet or should be it be treated as a black
  371.   // box? Ideally the Sheet is this abstract container and we're only
  372.   // concerned with our pages [dialogs]. However, there are scenarios where
  373.   // we might want to subclass it. For example, if the sheet is used as a 
  374.   // client of a framewindow and 'ShrinkToClient' is enabled, we'll need to
  375.   // detect when the sheet is resized (i.e. receiving WM_SIZE messages) to
  376.   // allow the frame to adjust.
  377.   //
  378.   if (SubClassSheet) {
  379.     SubclassWindowFunction();
  380.     GetHWndState(true);
  381.   }
  382.  
  383.   // Here we 'fake' a SetupWindow call - The typical OWL run-through
  384.   // (i.e. SetupWindow invoked off WM_CREATE) fails in this case since
  385.   // the sheet is created indirectly.
  386.   //
  387.   SetupWindow();
  388. }
  389.  
  390. //
  391. // Adds a new page to the end of the PropertySheet.
  392. // NOTE: The 'pg' must have been created via a call to
  393. //       'TPropertyPage::CreatePropertyPage' before
  394. //       invoking the 'AddPage' method.
  395. // NOTE: The property sheet is not resized to fit the new page. The
  396. //       new page should be no larger that the largest page already
  397. //       in the property sheet.
  398. //
  399. void
  400. TPropertySheet::AddPage(TPropertyPage& pg)
  401. {
  402.   // Update pointer to parent object
  403.   //
  404.   if (pg.GetParentO() != this)
  405.     pg.SetParent(this);
  406.  
  407. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  408.  
  409.   if (!UseNative) {
  410.     // Create the page
  411.     //
  412.     pg.Create();
  413.  
  414.     // Save active page
  415.     //
  416.     TPropertyPage* active = GetActivePage();
  417.  
  418.     // Update tabs to match pages
  419.     // NOTE: Not efficient!! (Revisit and improve)
  420.     //
  421.     SyncTabAndPages();
  422.  
  423.     // Restore active page
  424.     //
  425.     SetActivePage(IndexOfPage(active));
  426.     return;
  427.   }
  428. #endif
  429.  
  430.   // Have page create itself it necessary
  431.   //
  432.   pg.CreatePropertyPage();
  433.   CHECK(HPROPSHEETPAGE(pg));
  434.  
  435.   // Inform sheet about new page
  436.   //
  437.   CHECK(HWND(*this));
  438.   SendMessage(PSM_ADDPAGE, 0, TParam2(HPROPSHEETPAGE(pg)));
  439. }
  440.  
  441. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  442. //
  443. // Internal routine used to send notifications to each page
  444. //
  445. static bool
  446. sendApply(TPropertyPage* pPage, TPshNotify* notInfo)
  447. {
  448.   // Page may not have been created yet!
  449.   //
  450.   if (pPage->GetHandle())
  451.     return pPage->SendNotification(PropPageID, *notInfo) != PSNRET_NOERROR;
  452.   return false;
  453. }
  454. #endif
  455.  
  456. //
  457. // Simulates the choice of the Apply button, indicating that one or more
  458. // pages have changed and the changes need to be validated or recorded.
  459. // The property sheet sends the PSN_KILLACTIVE notification message to the
  460. // current page. If the current page returns FALSE, the propertysheet
  461. // sends the PSN_APPLY notification message to all pages.
  462. // Returns true if all pages successfully applied the changes or false
  463. // otherwise.
  464. //
  465. bool
  466. TPropertySheet::Apply()
  467. {
  468. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  469.   if (!UseNative) {
  470.     TPropertyPage* activePg = GetActivePage();
  471.     TPshNotify info(*this, 0, PSN_KILLACTIVE, 0);
  472.     if (activePg->SendNotification(PropPageID, info) == PSNRET_NOERROR) {
  473.       info.hdr.code = PSN_APPLY;
  474.       TPropertyPage* page = CONST_CAST(TPropertySheet*,
  475.                 this)->FirstPageThat(TCondPageFunc(sendApply), &info);
  476.  
  477.       // Disable Apply button if all pages successfully applied
  478.       //
  479.       if (page == 0) {
  480.         HWND btn = GetDlgItem(ID_APPLY);
  481.         if (btn)
  482.           ::EnableWindow(btn, FALSE);
  483.       }
  484.       return page == 0;
  485.     }
  486.     return false;
  487.   }
  488. #endif
  489.  
  490.   CHECK(HWND(*this));
  491.   return SendMessage(PSM_APPLY) != 0;
  492. }
  493.  
  494. //
  495. // Disables the 'Cancel' button and changes the text of the 'OK' button
  496. // to 'Close'. You must invoke this method after applying a change that
  497. // cannot be canceled.
  498. //
  499. void
  500. TPropertySheet::CancelToClose()
  501. {
  502. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  503.   if (!UseNative) {
  504.     TAPointer<char> p = new char[_MAX_PATH];
  505.     SetDlgItemText(IDOK, GetModule()->LoadString(IDS_CLOSE, p, _MAX_PATH) ? 
  506.                          (char*)p : CloseStr);
  507.     ::EnableWindow(GetDlgItem(IDCANCEL), FALSE);
  508.     return;
  509.   }
  510. #endif
  511.  
  512.   CHECK(HWND(*this));
  513.   SendMessage(PSM_CANCELTOCLOSE);
  514. }
  515.  
  516. //
  517. // Informs the sheet that information in a sheet has changed. The
  518. // sheet enables the 'Apply' button.
  519. //
  520. void
  521. TPropertySheet::PageChanged(const TPropertyPage& pg)
  522. {
  523. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  524.   if (!UseNative) {
  525.     HWND btn = GetDlgItem(ID_APPLY);
  526.     if (btn) {
  527.       if (!::IsWindowEnabled(btn))
  528.         ::EnableWindow(btn, TRUE);
  529.     }
  530.     return;
  531.   }
  532. #endif
  533.  
  534.   PRECONDITION(HPROPSHEETPAGE(pg));
  535.   SendMessage(PSM_CHANGED, TParam1(pg.GetHandle()));
  536. }
  537.  
  538. //
  539. // Retrieves the handle to the window of the current page of the sheet.
  540. //
  541. HWND
  542. TPropertySheet::GetCurrentPage() const
  543. {
  544. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  545.   if (!UseNative) {
  546.     TPropertyPage* activePage = GetActivePage();
  547.     return activePage ? activePage->GetHandle() : 0;  
  548.   }
  549. #endif
  550.  
  551.   return HWND(CONST_CAST(TPropertySheet*,
  552.                          this)->SendMessage(PSM_GETCURRENTPAGEHWND));
  553. }
  554.  
  555. //
  556. // Retrieves the handle to a tab control of a property sheet.
  557. //
  558. HWND
  559. TPropertySheet::GetTabControl() const
  560. {
  561. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  562.   if (!UseNative) {
  563.     PRECONDITION(Tab);
  564.     return Tab->GetHandle();
  565.   }
  566. #endif
  567.  
  568.   CHECK(GetHandle());
  569.   return HWND(CONST_CAST(TPropertySheet*,
  570.                          this)->SendMessage(PSM_GETTABCONTROL));
  571. }
  572.  
  573. //
  574. // Passes a message to a property sheet dialog box and indicates
  575. // whether the dialog processed the message.
  576. // Returns true if the message was processed or false otherwise.
  577. //
  578. bool
  579. TPropertySheet::IsDialogMessage(MSG& msg)
  580. {
  581. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  582.   if (!UseNative) {
  583.     // Not necessary when OWL provides underlying implementation since
  584.     // TPropertySheet can use the 'PreProcessMsg' method to intercept
  585.     // and handle desired events.
  586.     //
  587.     return false;
  588.   }
  589. #endif
  590.  
  591.   CHECK(GetHandle());
  592.   return SendMessage(PSM_ISDIALOGMESSAGE, 0, TParam2(&msg)) != 0;
  593. }
  594.  
  595. //
  596. // Simulates the choice of a property sheet button.
  597. // The button parameter can be one of the following:
  598. //
  599. //    PSBTN_APPLYNOW      Apply Now button.
  600. //    PSBTN_BACK          Back button.
  601. //    PSBTN_CANCEL        Cancel button.
  602. //    PSBTN_FINISH        Finish button.
  603. //    PSBTN_HELP          Help button.
  604. //    PSBTN_NEXT          Next button.
  605. //    PSBTN_OK            OK button
  606. //
  607. void
  608. TPropertySheet::PressButton(int button)
  609. {
  610. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  611.   if (!UseNative) {
  612.     uint id = 0;
  613.     switch (button) {
  614.       case PSBTN_APPLYNOW: id = ID_APPLY;  break;
  615.       case PSBTN_BACK:     id = ID_BACK;   break;
  616.       case PSBTN_CANCEL:   id = IDCANCEL;  break;
  617.       case PSBTN_FINISH:   id = ID_FINISH; break;
  618.       case PSBTN_HELP:     id = IDHELP;    break;
  619.       case PSBTN_NEXT:     id = ID_NEXT;   break;
  620.       case PSBTN_OK:       id = IDOK;      break;
  621.     }
  622.     if (id) {
  623.       // Could optimize here and use HandleMessage instead to avoid
  624.       // going through Windows msg. queue.
  625.       //
  626.       SendNotification(id, BN_CLICKED, GetDlgItem(id));
  627.     }
  628.     return;
  629.   }
  630. #endif
  631.   CHECK(GetHandle());
  632.   SendMessage(PSM_PRESSBUTTON, TParam1(button));
  633. }
  634.  
  635. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  636. //
  637. // Internal routine used to send notifications to each page
  638. //
  639. static bool
  640. sendQuerySiblings(TPropertyPage* pPage, MSG* msg)
  641. {
  642.   LRESULT result = pPage->SendMessage(PSM_QUERYSIBLINGS, msg->wParam, 
  643.                                                          msg->lParam);
  644.   if (result) {
  645.     msg->lParam = result;
  646.     return true;
  647.   }
  648.   return false;
  649. }
  650. #endif
  651.  
  652. //
  653. // Forwards the 'PSM_QUERYSIBLINGS' message to each page in the
  654. // property sheet. If a page returns a nonzeroe value, the property
  655. // sheet does not send the message to subsequent pages.
  656. // Returns the nonzero value from a page in the property sheet, or
  657. // zero if no page returns a nonzero value.
  658. //
  659. int
  660. TPropertySheet::QuerySiblings(TParam1 p1, TParam2 p2)
  661. {
  662. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  663.   if (!UseNative) {
  664.     MSG msg;
  665.     msg.message = PSM_QUERYSIBLINGS;
  666.     msg.wParam  = p1;
  667.     msg.lParam  = p2;
  668.     TPropertyPage* page = CONST_CAST(TPropertySheet*,
  669.               this)->FirstPageThat(TCondPageFunc(sendQuerySiblings), &msg);
  670.     return page ? int(msg.lParam) : 0;
  671.   }
  672. #endif
  673.   CHECK(GetHandle());
  674.   return (int)SendMessage(PSM_QUERYSIBLINGS, p1, p2);
  675. }
  676.  
  677. //
  678. // Indicates that the system needs to be restarted for the changes
  679. // to take effect. You should invoke this method only in response to
  680. // the PSN_APPLY or PSN_KILLACTIVE notifications.
  681. // NOTE: It's your responsibility to reboot the system [via ExitWindowEx
  682. //       for example].
  683. // NOTE: Invoking this method causes the TPropertySheet::Execute method
  684. //       to return ID_PSREBOOTSYSTEM.
  685. //
  686. void
  687. TPropertySheet::RebootSystem()
  688. {
  689. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  690.   if (!UseNative) {
  691.     if (IsFlagSet(wfModalWindow))
  692.       CloseWindow(ID_PSREBOOTSYSTEM);
  693.     return;
  694.   }
  695. #endif
  696.  
  697.   CHECK(GetHandle());
  698.   SendMessage(PSM_REBOOTSYSTEM);
  699. }
  700.  
  701. //
  702. // Removes the specified page from the property sheet
  703. //
  704. void
  705. TPropertySheet::RemovePage(TPropertyPage& pg)
  706. {
  707. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  708.   if (!UseNative) {
  709.     if (pg.GetSheet() == this) {
  710.       int index = IndexOfPage(&pg);
  711.       if (index != -1) {
  712.         pg.Destroy(-1);
  713.         Tab->Delete(index);
  714.         if (index == ActiveIndex && GetPageCount())
  715.           SetActivePage((ActiveIndex+1) % GetPageCount());
  716.       }
  717.     }
  718.     return;
  719.   }
  720. #endif
  721.  
  722.   PRECONDITION(HPROPSHEETPAGE(pg));
  723.   CHECK(GetHandle());
  724.   SendMessage(PSM_REMOVEPAGE, 0, TParam2(HPROPSHEETPAGE(pg)));
  725.   //
  726.   // Should we actually invoke 'DestroyPropertySheetPage' for
  727.   // Pages which are added then removed from the PropertySheet??
  728. }
  729.  
  730. //
  731. // Removes the page at the specified index from the property sheet
  732. //
  733. void
  734. TPropertySheet::RemovePage(int pgIndex)
  735. {
  736. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  737.   if (!UseNative) {
  738.     TPropertyPage* page = PageAtIndex(pgIndex);
  739.     if (page)
  740.       RemovePage(*page);
  741.     return;
  742.   }
  743. #endif
  744.  
  745.   CHECK(GetHandle());
  746.   SendMessage(PSM_REMOVEPAGE, pgIndex);
  747.   //
  748.   // Should we actually invoke 'DestroyPropertySheetPage' for
  749.   // Pages which are added then removed from the PropertySheet??
  750. }
  751.  
  752. //
  753. // Indicates that the system needs to be restarted for the changes
  754. // to take effect. You should invoke this method only in response to
  755. // the PSN_APPLY or PSN_KILLACTIVE notifications.
  756. // NOTE: It's your responsibility to reboot the system [via ExitWindowEx
  757. //       for example].
  758. // NOTE: Invoking this method causes the TPropertySheet::Execute method
  759. //       to return ID_PSRESTARTWINDOWS.
  760. //
  761. void
  762. TPropertySheet::RestartWindows()
  763. {
  764. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  765.   if (!UseNative) {
  766.     if (IsFlagSet(wfModalWindow))
  767.       CloseWindow(ID_PSRESTARTWINDOWS);
  768.     return;
  769.   }
  770. #endif
  771.  
  772.   CHECK(GetHandle());
  773.   SendMessage(PSM_RESTARTWINDOWS);
  774. }
  775.  
  776. //
  777. // Activates the specified page in the property sheet
  778. // Returns true if successful or false otherwise.
  779. // NOTE: The page that's loosing activation receives a PSN_KILLACTIVE
  780. //       notification while the window that's gaining activation receives
  781. //       a PSN_SETACTIVE notification.
  782. //
  783. bool
  784. TPropertySheet::SelectPage(TPropertyPage& pg)
  785. {
  786. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  787.   if (!UseNative) {
  788.     TPshNotify info(*this, 0, PSN_KILLACTIVE, 0);
  789.     TPropertyPage* page = GetActivePage();
  790.     if (page && page->SendNotification(PropPageID, info) != PSNRET_NOERROR) 
  791.       return false;
  792.  
  793.     int index = IndexOfPage(&pg);
  794.     if (index != -1) {
  795.       SetActivePage(index);
  796.       info.hdr.code = PSN_SETACTIVE;
  797.       pg.SendNotification(PropPageID, info);
  798.       return true;
  799.     }
  800.     return false;
  801.   }
  802. #endif
  803.  
  804.   PRECONDITION(GetHandle());
  805.   PRECONDITION(HPROPSHEETPAGE(pg));
  806.   return SendMessage(PSM_SETCURSEL, 0, TParam2(HPROPSHEETPAGE(pg))) != 0;
  807. }
  808.  
  809. //
  810. // Activates the page at the specified index in the property sheet.
  811. // Returns true if successful or false otherwise.
  812. // NOTE: The page that's loosing activation receives a PSN_KILLACTIVE
  813. //       notification while the window that's gaining activation receives
  814. //       a PSN_SETACTIVE notification.
  815. //
  816. bool
  817. TPropertySheet::SelectPage(int pgIndex)
  818. {
  819. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  820.   if (!UseNative) {
  821.     PRECONDITION(pgIndex < GetPageCount());
  822.     TPropertyPage* page = PageAtIndex(pgIndex);
  823.     if (page)
  824.       return SelectPage(*page);
  825.     return false;
  826.   }
  827. #endif
  828.  
  829.   PRECONDITION(GetHandle());
  830.   return SendMessage(PSM_SETCURSEL, TParam1(pgIndex)) != 0;
  831. }
  832.  
  833. //
  834. // Activates the page with the specified resource identifier.
  835. // Returns true if successful or false otherwise.
  836. // NOTE: The page that's loosing activation receives a PSN_KILLACTIVE
  837. //       notification while the window that's gaining activation receives
  838. //       a PSN_SETACTIVE notification.
  839. //
  840. bool
  841. TPropertySheet::SelectPage(TResId pgRes)
  842. {
  843. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  844.   if (!UseNative) {
  845.     // This flavour of page selection is not supported by the ObjectWindows
  846.     // emulation of PropertySheet/PropertyPage. 
  847.     return false;
  848.   }
  849. #endif
  850.  
  851.   CHECK(GetHandle());
  852.   return SendMessage(PSM_SETCURSELID, 0, TParam2((char far*)pgRes));
  853. }
  854.  
  855. //
  856. // Sets the text for the 'Finish' button in a Wizard property sheet.
  857. // NOTE: The button is enabled while the 'Next' and 'Back' buttons
  858. //       are hidden.
  859. //
  860. void
  861. TPropertySheet::SetFinishText(const char far* txt)
  862. {
  863. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  864.   if (!UseNative) {
  865.     HWND finish = GetDlgItem(ID_FINISH);
  866.     HWND next   = GetDlgItem(ID_NEXT);
  867.     HWND back   = GetDlgItem(ID_BACK);
  868.     if (finish)
  869.       ::SetWindowText(finish, txt);
  870.     if (next)
  871.       ::EnableWindow(next, FALSE);  // Disable (rather than hide)
  872.     if (back)
  873.       ::EnableWindow(back, FALSE);  // Disable (rather than hide)
  874.     return;
  875.   }
  876. #endif
  877.  
  878.   CHECK(GetHandle());
  879.   SendMessage(PSM_SETFINISHTEXT, 0, TParam2(txt));
  880. }
  881.  
  882. //
  883. // Sets the title of a property sheet.
  884. // If 'style' parameter is the PSH_PROPTITLE value, the prefix
  885. // "Properties of" is included with the specified title ('txt')
  886. // parameter.
  887. //
  888. void
  889. TPropertySheet::SetTitle(const char far* txt, uint32 style)
  890. {
  891. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  892.   if (!UseNative) {
  893.     SetWindowText(txt);
  894.     return;
  895.   }
  896. #endif
  897.  
  898.   CHECK(GetHandle());
  899.   SendMessage(PSM_SETTITLE, TParam1(style), TParam2(txt));
  900. }
  901.  
  902. //
  903. // Enables the 'Back', 'Next' or 'Finish' button in a wizard
  904. // property sheet. The 'flags' parameter can be a combination of
  905. // the following values:
  906. //
  907. //          PSWIZB_BACK                 Back button
  908. //          PSWIZB_NEXT                 Next button
  909. //          PSWIZB_FINISH               Finish button
  910. //
  911. void
  912. TPropertySheet::SetWizButtons(uint32 flags)
  913. {
  914. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  915.   if (!UseNative) {
  916.     HWND finish = GetDlgItem(ID_FINISH);
  917.     HWND next   = GetDlgItem(ID_NEXT);
  918.     HWND back   = GetDlgItem(ID_BACK);
  919.     if (back)
  920.       ::EnableWindow(back, (flags & PSWIZB_BACK) ? TRUE : FALSE);
  921.     if (next)
  922.       ::EnableWindow(next, (flags & PSWIZB_NEXT) ? TRUE : FALSE);
  923.     if (finish)
  924.       ::EnableWindow(finish, (flags & PSWIZB_FINISH) ? TRUE : FALSE);
  925.     return;
  926.   }
  927. #endif
  928.  
  929.   CHECK(GetHandle());
  930.   SendMessage(PSM_SETWIZBUTTONS, 0, TParam2(flags));
  931. }
  932.  
  933. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  934. //
  935. // Internal routine used to check if there's a dirty page
  936. //
  937. static bool
  938. isModified(TPropertyPage* pPage, void*)
  939. {
  940.   return pPage->IsModified();
  941. }
  942. #endif
  943.  
  944. //
  945. // Informs the sheet that the information in the specified page
  946. // has reverted to the previously saved state. The sheet disables
  947. // the 'Apply' button if no other pages have registered changes
  948. // with the property sheet.
  949. //
  950. void
  951. TPropertySheet::PageUnchanged(TPropertyPage& pg)
  952. {
  953. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  954.   if (!UseNative) {
  955.  
  956.     // Update the specified page
  957.     //
  958.     pg.SetModified(false);
  959.  
  960.     // Find out if there are any dirty pages
  961.     //
  962.     TPropertyPage* page = CONST_CAST(TPropertySheet*,
  963.                           this)->FirstPageThat(isModified, 0);
  964.  
  965.     // Disable APPLY button if all pages are clean
  966.     //
  967.     if (!page) {
  968.       HWND apply = GetDlgItem(ID_APPLY);
  969.       if (apply)
  970.         ::EnableWindow(apply, FALSE);
  971.     }
  972.     return;
  973.   }
  974. #endif
  975.  
  976.   PRECONDITION(HPROPSHEETPAGE(pg));
  977.   CHECK(GetHandle());
  978.   SendMessage(PSM_UNCHANGED, TParam1(pg.GetHandle()));
  979. }
  980.  
  981. //
  982. //
  983. //
  984. bool
  985. TPropertySheet::PreProcessMsg(MSG& msg) {
  986.  
  987.   // If current page = 0, then it's time to close the property sheet.
  988.   // 
  989.   HWND page = GetCurrentPage();
  990.   if (!page) {
  991.     CloseWindow();
  992.     return false;
  993.   } 
  994.   else {
  995.     return TWindow::PreProcessMsg(msg);
  996.   }
  997. }
  998.  
  999. // ---------------------------------------------------------------------------
  1000. //  TPropertyPage
  1001. // ---------------------------------------------------------------------------
  1002.  
  1003. DEFINE_RESPONSE_TABLE1(TPropertyPage, TDialog)
  1004.   EV_PSN_SETACTIVE(SetActive),
  1005.   EV_PSN_KILLACTIVE(KillActive),
  1006.   EV_PSN_APPLY(Apply),
  1007.   EV_PSN_RESET(Reset),
  1008.   EV_PSN_HELP(Help),
  1009.   EV_PSN_WIZBACK(WizBack),
  1010.   EV_PSN_WIZFINISH(WizFinish),
  1011.   EV_PSN_WIZNEXT(WizNext),
  1012.   EV_PSN_QUERYCANCEL(QueryCancel),
  1013.  
  1014.   EV_COMMAND(IDOK, CmOk),
  1015.   EV_COMMAND(IDCANCEL, CmCancel),
  1016. END_RESPONSE_TABLE;
  1017.  
  1018. //
  1019. // Constructor for TPropertyPage
  1020. //
  1021. //
  1022. TPropertyPage::TPropertyPage(TPropertySheet* parent, TResId resId,
  1023.                              const char far* title, TResId iconRes,
  1024.                              TModule* module)
  1025.   TDialog(parent, resId, module), HPropPage(0)
  1026. {
  1027.   // Initialize the PROPSHEETPAGE structure
  1028.   // NOTE: We're storing the 'this' pointer in the application-defined
  1029.   //       section of the PROPSHEETPAGE structure...
  1030.   //
  1031.   memset(&PageInfo, 0, sizeof(PageInfo));
  1032.   PageInfo.dwSize = sizeof(PROPSHEETPAGE);
  1033.   PageInfo.dwFlags= PSP_DEFAULT;
  1034.   PageInfo.pszTemplate = resId;
  1035.   PageInfo.hInstance = *GetModule();
  1036.  
  1037.   if (title)
  1038.     SetTitle(title);
  1039.   if (iconRes)
  1040.     SetIcon(iconRes);
  1041.  
  1042.   PageInfo.dwFlags |= PSP_USECALLBACK;
  1043.   PageInfo.pfnCallback = PropCallback;
  1044.   PageInfo.pfnDlgProc = PropDlgProc;
  1045.   PageInfo.lParam = LPARAM(this);
  1046.  
  1047. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  1048.  
  1049.   Modified = false;
  1050.  
  1051.   // By default, we'll use the native implementation if it's available
  1052.   //
  1053.   UseNative = TCommCtrl::IsAvailable();
  1054.  
  1055. #else
  1056.   // When OWL is built with the NATIVECTRL_ALWAYS option or in 32-bit the
  1057.   // Common Control library MUST be available....
  1058.   //
  1059.   CHECK(TCommCtrl::IsAvailable());
  1060. #endif
  1061. }
  1062.  
  1063.  
  1064. //
  1065. // Destructor of TPropertyPage - Clean up allocated buffers used when
  1066. // ObjectWindows provides implementation of property pages.
  1067. //
  1068. TPropertyPage::~TPropertyPage()
  1069. {}
  1070.  
  1071. //
  1072. // Specifies flags to be used in creating the property page.  These
  1073. // are the flags that belong in PROPSHEETPAGE.dwFlags.  If used, this 
  1074. // method should be called immediately after the TPropertyPage is 
  1075. // constructed.
  1076. //
  1077. void
  1078. TPropertyPage::SetFlags(uint32 flags)
  1079. {
  1080.   PageInfo.dwFlags = flags;
  1081. }
  1082.  
  1083. //
  1084. // Specifies the icon to be used for this page
  1085. // NOTE: This routine must be invoked before the page is created.
  1086. //
  1087. void
  1088. TPropertyPage::SetIcon(const TIcon& icon)
  1089. {
  1090.   PageInfo.hIcon = icon;
  1091.   PageInfo.dwFlags &= ~PSH_USEICONID;
  1092.   PageInfo.dwFlags |=  PSH_USEHICON;
  1093. }
  1094.  
  1095. //
  1096. // Specifies the icon to be used for this page
  1097. // NOTE: This routine must be invoked before the page is created.
  1098. //
  1099. void
  1100. TPropertyPage::SetIcon(TResId iconResId)
  1101. {
  1102.   PageInfo.pszIcon = iconResId;
  1103.   PageInfo.dwFlags &= ~PSH_USEHICON;
  1104.   PageInfo.dwFlags |=  PSH_USEICONID;
  1105. }
  1106.  
  1107. //
  1108. // Sets the caption of this page.
  1109. // NOTE: This routine must be invoked before the page is created.
  1110. //
  1111. void
  1112. TPropertyPage::SetTitle(const char far* title)
  1113. {
  1114.   // Let TWindow make a copy of the title to 'Title'
  1115.   // Then point to the 'duped' copy...
  1116.   //
  1117.   SetCaption(title);
  1118.   PageInfo.pszTitle = Title;
  1119.   PageInfo.dwFlags |= PSP_USETITLE;
  1120. }
  1121.  
  1122. //
  1123. // Sets the caption of this page.
  1124. // NOTE: This routine must be invoked before the page is created.
  1125. //
  1126. void
  1127. TPropertyPage::SetTitle(int txtResId)
  1128. {
  1129.   PageInfo.pszTitle = MAKEINTRESOURCE(txtResId);
  1130.   PageInfo.dwFlags &= ~PSP_USETITLE;
  1131. }
  1132.  
  1133. //
  1134. // WM_NOTIFY handler: Scans for property sheet notifications to 'patch'
  1135. // the 'idFrom' member to the predefined 'PropPageID'.
  1136. // NOTE: This is necessary since WM_NOTIFY subdispatching relies on the
  1137. //       id of the sender.
  1138. //
  1139. TResult
  1140. TPropertyPage::EvNotify(uint id, TNotify& notifyInfo)
  1141. {
  1142.   if (notifyInfo.code <= PSN_FIRST && notifyInfo.code >= PSN_LAST) {
  1143.     // Property sheet notifications require special handling since the
  1144.     // concept of ctlId is non-existent. We patch it to the default
  1145.     // PageID expected by the ObjectWindows Property Page dispatchers
  1146.     //
  1147.     notifyInfo.idFrom = PropPageID;
  1148.     id = PropPageID;
  1149.  
  1150.     // Also make sure we don't reflect the message back to what looks like
  1151.     // the 'child' sender but is really the sheet. We achieve this by
  1152.     // NULLing out the HWND of the sender.
  1153.     //
  1154.     notifyInfo.hwndFrom = 0;
  1155.   }
  1156.   return TDialog::EvNotify(id, notifyInfo);
  1157. }
  1158.  
  1159. //
  1160. // This callback is the default 'Dialog box procedure' of each page of our
  1161. // property sheet....
  1162. //
  1163. int CALLBACK OWL_EXPORT16
  1164. TPropertyPage::PropDlgProc(HWND hDlg, uint msg, WPARAM wParam, LPARAM lParam)
  1165. {
  1166.   switch(msg) {
  1167.     case  WM_INITDIALOG: {
  1168.             // Attach C++ object with it's underlying handle if necessary
  1169.             //
  1170.             LPPROPSHEETPAGE pageInfo = REINTERPRET_CAST(LPPROPSHEETPAGE,
  1171.                                                         lParam);
  1172.             InitHandle(hDlg, pageInfo);
  1173.           }
  1174.           break;
  1175.  
  1176.     case WM_NOTIFY: {
  1177.           TNotify& notifyInfo= *(REINTERPRET_CAST(TNotify*, lParam));
  1178.           if (notifyInfo.code <= PSN_FIRST && notifyInfo.code >= PSN_LAST) {
  1179.  
  1180.               // Property sheet notifications require special handling
  1181.               // since the concept of ctlId is non-existent. We patch it
  1182.               // to the default PageID expected by the ObjectWindows
  1183.               // Property Page dispatchers
  1184.               //
  1185.               notifyInfo.idFrom = PropPageID;
  1186.               wParam = PropPageID;
  1187.             }
  1188.           }
  1189.           break;
  1190.  
  1191.     default:
  1192.           break;
  1193.   }
  1194.  
  1195.   return TDialog::StdDlgProc(hDlg, msg, wParam, lParam);
  1196. }
  1197.  
  1198. //
  1199. // As with TDialog, most of the page's events are dispatch
  1200. // directly from 'StdWndProc'. ALthough the Sheet has each
  1201. // page's DialogProc, the notifications are not (don't seem
  1202. // to be) funelled directly to the dialogProc
  1203. //
  1204. bool
  1205. TPropertyPage::DialogFunction(uint msg, TParam1 p1, TParam2 p2)
  1206. {
  1207.   switch(msg) {
  1208.     case WM_NOTIFY: {
  1209.            NMHDR& nmhdr = *(REINTERPRET_CAST(NMHDR far*, p2));
  1210.            if (nmhdr.code >= PSN_LAST && nmhdr.code <= PSN_FIRST) {
  1211.              CHECK(p1 != 0);
  1212.              CHECK(nmhdr.idFrom != 0);
  1213.            }
  1214.          }
  1215.          break;
  1216.  
  1217.     default:
  1218.         break;
  1219.   }
  1220.   return TDialog::DialogFunction(msg, p1, p2);
  1221. }
  1222.  
  1223. // 'CopyPageInfo' is called by the 'Sheet' object requesting
  1224. // the page to fill out a 'PROPSHEETPAGE' structure which
  1225. // describes the attribute of the page.
  1226. //
  1227. void
  1228. TPropertyPage::CopyPageInfo(PROPSHEETPAGE& pgInfo) const
  1229. {
  1230.   pgInfo = PageInfo;
  1231. }
  1232.  
  1233. //
  1234. // 'CreatePropertyPage' is called by the 'Sheet' object requesting
  1235. // the page to return a handle used to represent this dialog
  1236. // when it's inserted into the 'Sheet'
  1237. //
  1238. HPROPSHEETPAGE
  1239. TPropertyPage::CreatePropertyPage()
  1240. {
  1241.   if (!HPropPage) 
  1242.     HPropPage = TCommCtrl::Dll()->CreatePropertySheetPage(&PageInfo);
  1243.  
  1244.   return HPropPage;
  1245. }
  1246.  
  1247. //
  1248. // Destroys the page represented by this object.
  1249. //
  1250. bool
  1251. TPropertyPage::DestroyPropertyPage()
  1252. {
  1253.   PRECONDITION(HPropPage);
  1254.   if (TCommCtrl::Dll()->DestroyPropertySheetPage(HPropPage)) {
  1255.     HPropPage= 0;
  1256.     return true;
  1257.   }
  1258.   return false;
  1259. }
  1260.  
  1261. //
  1262. // Creates the page
  1263. //
  1264. bool
  1265. TPropertyPage::Create()
  1266. {
  1267. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  1268.   //
  1269.   // If we're using ObjectWindows' implementation of PropertySheet/Page,
  1270.   // simply chain to the default TDialog::Create method
  1271.   //
  1272.   if (!UseNative)
  1273.     return TDialog::Create();
  1274. #endif
  1275.  
  1276.   // When using the system's implementation if PropertyDialogs, the page
  1277.   // is actually created behind the scene when the PropertySheet is
  1278.   // created. The callbacks specified by ObjectWindows [PropDlgProc &
  1279.   // PropCallback] will update the TPropertyPage's HWindow data member.
  1280.   //
  1281.   // Therefore, our 'Create' method simply checks that the handle was
  1282.   // indeed initialized and happily returns true.
  1283.   //
  1284.   CHECK(GetHandle() != 0);
  1285.   return true;
  1286. }
  1287.  
  1288. //
  1289. // The pages of a propertysheet are created internally by windows...
  1290. // Consequently, we must attempt to grab and thunk the 'HWND' as
  1291. // early as possible. There are two basic opportunities to do so:
  1292. //
  1293. // (a) A Sheet can provide a callback which is called whenever a
  1294. //     page is created or released. Hence 'TPropertyPage::PropCallback'
  1295. //     invokes 'InitHandle'.
  1296. //
  1297. // (b) Each page provides a dialog-procedure callback. Hence,
  1298. //     'TPropertyPage::PropDlgProc' invokes 'InitHandle' upon receiving
  1299. //     a WM_INITDIALOG message.
  1300. //
  1301. void
  1302. TPropertyPage::InitHandle(HWND pageHandle, LPPROPSHEETPAGE ppsp)
  1303. {
  1304.   // First check that the lParam data member of the PROPSHEETPAGE
  1305.   // contains a 'this' pointer to an OWL TPropertyPage object
  1306.   // wrapping the dialog
  1307.   //
  1308.   TPropertyPage* page = REINTERPRET_CAST(TPropertyPage*, ppsp->lParam);
  1309.  
  1310.   if (page && pageHandle) {
  1311.     // Only proceed if the C++ object is not fully initialized
  1312.     //
  1313.     if (page->GetHandle()) {
  1314.       CHECK(page->GetHandle() == pageHandle);
  1315.       return;
  1316.     }
  1317.  
  1318.     // Proceed to initialize the handle of the page object.
  1319.     //
  1320.     page->SetHandle(pageHandle);
  1321.  
  1322.     // We can now retrieve the pointer to the sheet object
  1323.     // and initialize the latter if necessary.
  1324.     //
  1325.     TPropertySheet* sheet = page->GetSheet();
  1326.     if (sheet) {
  1327.       if (!sheet->GetHandle()) {
  1328.         HWND sheetHandle = ::GetParent(pageHandle);
  1329.         if (sheetHandle)
  1330.           sheet->InitHandle(sheetHandle);
  1331.       }
  1332.     }
  1333.  
  1334.     // Allow OWL to thunk the page window.
  1335.     //
  1336.     page->SubclassWindowFunction();
  1337.  
  1338.     // NOTE: Typically, we'd call 'GetHWndState', 'PerformDlgInit',
  1339.     //       'SetupWindow' and 'SetFlag(wfFullyCreated)' after
  1340.     //       thunking a window. However, TDialog's 'EvInitDialog'
  1341.     //       method [invoked via the PropDlgProc callback] will
  1342.     //       handle that.
  1343.   }
  1344. }
  1345.  
  1346. //
  1347. // Static callback invoked whenever a Property Page is created is
  1348. // destroyed.
  1349. //
  1350. uint CALLBACK OWL_EXPORT16
  1351. TPropertyPage::PropCallback(HWND hwnd, uint uMsg, LPPROPSHEETPAGE ppsp)
  1352. {
  1353.   switch (uMsg) {
  1354.     case PSPCB_CREATE: {
  1355.  
  1356.            // A Property Page was just created.. We'll attempt to thunk
  1357.            // the underlying 'HWND' if it was specified..
  1358.            //
  1359.            if (hwnd)
  1360.              InitHandle(hwnd, ppsp);
  1361.          }
  1362.          break;
  1363.  
  1364.     case PSPCB_RELEASE: {
  1365.  
  1366.             // A Property Page was just released... Currently, we
  1367.             // don't have any processing to do here....
  1368.             // Should we invoke a virtual method of TPropertyPage
  1369.             // from here - just in case a page needs to do something
  1370.             // when it's released or are the default ObjectWindows
  1371.             // mechanisms (CleanupWindow & Destructor) sufficient....
  1372.             //
  1373.          }
  1374.          break;
  1375.  
  1376.     default:
  1377.           break;
  1378.   }
  1379.   // The return is ignored for PSPCB_RELEASE (according to the doc).
  1380.   // A non-zero value allows the page to be created...
  1381.   //
  1382.   return 1;
  1383. }
  1384.  
  1385. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  1386. //
  1387. // Returns the caption of a PropertyPage
  1388. // May be used for PropertyPages with or without an underlying
  1389. // window element.
  1390. //
  1391. bool
  1392. TPropertyPage::GetPageCaption(char* txt, int size)
  1393. {
  1394.   // This method should only be invoked when ObjectWindows
  1395.   // is providing the underlying implementation of Property Page.
  1396.   //
  1397.   CHECK(!UseNative);
  1398.  
  1399.   // Retrieve actual text if page has been created
  1400.   //
  1401.   if (GetHandle()) {
  1402.     return GetWindowText(txt, size) != 0;
  1403.   }
  1404.   else {
  1405.     if ((PageInfo.dwFlags & PSP_USETITLE) && Title) {
  1406.       strncpy(txt, Title, size);
  1407.     }
  1408.     else {
  1409.       PRECONDITION((char*)ResCaption && *((char*)ResCaption));
  1410.       strncpy(txt, ResCaption, size);
  1411.     }
  1412.   }
  1413.   return true;
  1414. }
  1415.  
  1416. //
  1417. // Sets the caption of a PropertyPage
  1418. //
  1419. void
  1420. TPropertyPage::SetPageCaption(char far* caption)
  1421. {
  1422.   PRECONDITION(caption);
  1423.   PRECONDITION(*caption);
  1424.  
  1425.   // This method should only be invoked when ObjectWindows
  1426.   // is providing the underlying implementation of Property Page.
  1427.   //
  1428.   CHECK(!UseNative);
  1429.   ResCaption = nstrnewdup(caption);
  1430. }
  1431.  
  1432. #endif
  1433.  
  1434. // Default implementation of PropertySheet notifications.. Derived classes
  1435. // will override - most likely.
  1436. //
  1437.  
  1438. //
  1439. //
  1440. //
  1441. int
  1442. TPropertyPage::Apply(TNotify&)
  1443. {
  1444. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  1445.   if (!UseNative) {
  1446.     // Clear modified flag 
  1447.     //
  1448.     SetModified(false);
  1449.   }
  1450. #endif
  1451.  
  1452.   // Check if it's OK to close and attempt to retrieve data
  1453.   //
  1454.   if (CanClose()) {
  1455.     TransferData(tdGetData);
  1456.     return PSNRET_NOERROR;
  1457.   }
  1458.  
  1459.   // It's not OK to proceed - return focus to this page
  1460.   //
  1461.   return PSNRET_INVALID_NOCHANGEPAGE;
  1462. }
  1463.  
  1464. //
  1465. //
  1466. //
  1467. bool
  1468. TPropertyPage::KillActive(TNotify&)
  1469. {
  1470.   return false;
  1471. }
  1472.  
  1473. //
  1474. //
  1475. //
  1476. void
  1477. TPropertyPage::Help(TNotify&)
  1478. {
  1479. }
  1480.  
  1481. //
  1482. //
  1483. //
  1484. void
  1485. TPropertyPage::Reset(TNotify&)
  1486. {
  1487. }
  1488.  
  1489. //
  1490. //
  1491. //
  1492. int
  1493. TPropertyPage::SetActive(TNotify&)
  1494. {
  1495.   return 0;
  1496. }
  1497.  
  1498. //
  1499. //
  1500. //
  1501. int
  1502. TPropertyPage::WizBack(TNotify&)
  1503. {
  1504.   return 0;
  1505. }
  1506.  
  1507. //
  1508. //
  1509. //
  1510. bool
  1511. TPropertyPage::WizFinish(TNotify&)
  1512. {
  1513.   return false;
  1514. }
  1515.  
  1516. //
  1517. //
  1518. //
  1519. int
  1520. TPropertyPage::WizNext(TNotify&)
  1521. {
  1522.   return 0;
  1523. }
  1524.  
  1525. //
  1526. //
  1527. //
  1528. bool
  1529. TPropertyPage::QueryCancel(TNotify&)
  1530. {
  1531.   return false;
  1532. }
  1533.  
  1534. // ------------------------------------------------------------------------
  1535. // The following routines are used by ObjectWindows when it
  1536. // provides the underlying support for PropertyPage
  1537. // ------------------------------------------------------------------------
  1538. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  1539.  
  1540. //
  1541. // Responds to an incoming notification message from a button with
  1542. // an Id equal to IDOK
  1543. //
  1544. void
  1545. TPropertyPage::CmOk()
  1546. {
  1547.   if (!UseNative) {
  1548.     TPropertySheet* sheet = GetSheet();
  1549.     if (sheet) {
  1550.       sheet->Apply();
  1551.       sheet->CloseWindow(IDOK);
  1552.     }
  1553.   }
  1554.   else
  1555.     DefaultProcessing();
  1556. }
  1557.  
  1558. //
  1559. //
  1560. void
  1561. TPropertyPage::CmCancel()
  1562. {
  1563.   TPropertySheet* sheet = GetSheet();
  1564.   if (sheet)
  1565.     sheet->CloseWindow(IDCANCEL);
  1566. }
  1567.  
  1568. //
  1569. // Update the style/size of the dialog [PropertyPage]
  1570. //
  1571. void
  1572. TPropertyPage::SetupWindow()
  1573. {
  1574.   // Chain to base class' version - (Allows base class to perform
  1575.   // initialization such as transfer of control data)
  1576.   //
  1577.   TDialog::SetupWindow();
  1578.  
  1579.   if (!UseNative) {
  1580.     // Does this dialog have a caption
  1581.     //
  1582.     bool hasCaption = (GetStyle() & WS_CAPTION) != 0;
  1583.  
  1584.     // Strip off and add undesirable and desirable styles respectively
  1585.     //
  1586.     ModifyStyle(WS_BORDER|WS_CAPTION|WS_VISIBLE, WS_GROUP|WS_TABSTOP);
  1587.     ModifyExStyle(WS_EX_WINDOWEDGE, 0, SWP_DRAWFRAME);
  1588.  
  1589.     // Adjust dialog's height if we've truncated the caption
  1590.     //
  1591.     if (hasCaption) {
  1592.       TRect dlgRect;
  1593.       GetWindowRect(dlgRect);
  1594.       dlgRect.bottom -= TUIMetric::CyCaption - TUIMetric::CyBorder;
  1595.       SetWindowPos(HWND_BOTTOM, dlgRect, SWP_NOACTIVATE|SWP_NOMOVE);
  1596.     }
  1597.   }
  1598. }
  1599.  
  1600.  
  1601. #endif  
  1602.  
  1603. // ------------------------------------------------------------------------
  1604. // The following routines are used by ObjectWindows when it
  1605. // provides the underlying support for PropertySheet
  1606. // ------------------------------------------------------------------------
  1607. #if !defined(OWL_NATIVECTRL_ALWAYS) && !defined(BI_PLAT_WIN32)
  1608.  
  1609. const int MaxTabTextLen = 100;
  1610. const int ID_TabControl = 0x1000;
  1611.  
  1612. //
  1613. // Used internally [within this module] to match pages and indices
  1614. //
  1615. struct TPageInfo {
  1616.   const TPropertyPage* Page;      // Pointer to PropertyPage within sheet
  1617.   int                  Index;     // Index of the page [zero-based]
  1618. };
  1619.  
  1620. //
  1621. // NOTE: The 'rect' passed in should be initialized to (0,0,0,0)
  1622. //       before invoking this function for the first time...
  1623. //
  1624. static void
  1625. findDlgResInfo(TPropertyPage* page, TRect* rect)
  1626. {
  1627.   // Find/Load/Lock dialog resource and retrieve dimensions
  1628.   //
  1629.   TDialogRes dlgRes(*(page->GetModule()), page->DialogAttr.Name);
  1630.   TRect dlgRect;
  1631.   dlgRes.GetRect(dlgRect);
  1632.  
  1633.   // Store width of widest dialog
  1634.   //
  1635.   if (dlgRect.Width() > rect->Width()) {
  1636.     rect->left = dlgRect.left;
  1637.     rect->right= dlgRect.right;
  1638.   }
  1639.  
  1640.   // Store away the height if this is the tallest resource
  1641.   //
  1642.   if (dlgRect.Height() > rect->Height()) {
  1643.     rect->top = dlgRect.top;
  1644.     rect->bottom = dlgRect.bottom;
  1645.   }
  1646.  
  1647.   // Store away the page's caption
  1648.   //
  1649.   char caption[100];
  1650.   if (dlgRes.GetText(caption, sizeof(caption)))
  1651.     page->SetPageCaption(caption);
  1652. }
  1653.  
  1654. //
  1655. // Computes the size of the sheet, activates the designated 'startPage'
  1656. // and sets focus to the tab control...
  1657. //
  1658. void
  1659. TPropertySheet::SetupWindow()
  1660. {
  1661.   TWindow::SetupWindow();
  1662.  
  1663.   // Initialization when PropertySheet support is provided by ObjectWindows
  1664.   //
  1665.   if (!UseNative) {
  1666.  
  1667.     // Grab a copy of font based on system's UI
  1668.     //
  1669.     Font = new TDefaultGUIFont;
  1670.  
  1671.     // Create Tab control
  1672.     //
  1673.     Tab = new TTabControl(this, ID_TabControl, 0, 0, 100, 100);
  1674.     if (HeaderInfo.dwFlags & PSH_MULTILINETABS)
  1675.       Tab->SetStyle(Tab->GetStyle() | TCS_MULTILINE);
  1676.     Tab->Create();
  1677.  
  1678.     // Initialize Tab's attributes
  1679.     //
  1680.     if (Font)
  1681.       Tab->SetWindowFont(*Font, true);
  1682.  
  1683.     // Retrieve the size of biggest page/dialog
  1684.     // NOTE: As a side-effect, 'findDlgResInfo' also retrieves the
  1685.     //       caption of each dialog from it's resource template...
  1686.     //
  1687.     TRect rect(0, 0, 0, 0);
  1688.     ForEachPage(TActionPageFunc(findDlgResInfo), &rect);
  1689.  
  1690.     // Update tab with entries of each page and select the
  1691.     // tab corresponding to our 'startPage' a page
  1692.     //
  1693.     SyncTabAndPages();
  1694.     Tab->SetSel(HeaderInfo.nStartPage);
  1695.  
  1696.     // Activate the startPage index
  1697.     //
  1698.     SetActivePage(HeaderInfo.nStartPage);
  1699.  
  1700.     // The sizes retrieved from the dialog resources are in
  1701.     // dialog base units: We convert them to screen coordinates..
  1702.     // NOTE: Win95 seems to be aware of 3.1 style dialogs (i.e.
  1703.     //       non-DS_3DLOOK ones) and computes the dialog base units
  1704.     //       accordingly since these dialogs use the bold font.
  1705.     //       At this point we've retrieved the rectangle of the biggest
  1706.     //       dialog. However, the first active dialog is the one used
  1707.     //       to convert that size from DialogUnits to Pels... So mixing
  1708.     //       DS_3DLOOK and non-DS_3DLOOK dialogs with an app. marked
  1709.     //       for an earlier version of Windows may result (what am I
  1710.     //       saying..., WILL RESULT) in incorrect computation of the
  1711.     //       required dimensions...
  1712.     //
  1713.     PRECONDITION(GetActivePage());
  1714.     GetActivePage()->MapDialogRect(rect);
  1715.  
  1716.     // Retrieve sizes of buttons and margins
  1717.     //
  1718.     ComputeMarginAndButton();
  1719.  
  1720.     // Update the size of the sheet & tab and position the page
  1721.     //
  1722.     UpdateTabAndSheetSize(rect);
  1723.     PositionPage(GetActivePage());
  1724.  
  1725.     // Create Buttons; position them and update their state
  1726.     //
  1727.     CreateButtons();
  1728.     UpdateButtons();
  1729.  
  1730.     // Set Focus to tab control [Is this the desired behaviour?]
  1731.     //
  1732.     Tab->SetFocus();
  1733.   }
  1734. }
  1735.  
  1736. //
  1737. // Internal routine used to identify the page at a particular index
  1738. //
  1739. static bool
  1740. matchPageIndex(TPropertyPage* /*pPage*/, int* pArray)
  1741. {
  1742.   if (pArray[0] == pArray[1])
  1743.     return true;
  1744.   else
  1745.     pArray[0]++;
  1746.  
  1747.   return false;
  1748. }
  1749.  
  1750. //
  1751. // Returns a pointer to the object representing the page at the
  1752. // specified index. Returns zero if no page is found.
  1753. //
  1754. TPropertyPage*
  1755. TPropertySheet::PageAtIndex(int index) const
  1756. {
  1757.   PRECONDITION(index < GetPageCount());
  1758.   int counts[]={0, index};
  1759.   TPropertyPage* page = CONST_CAST(TPropertySheet*,
  1760.                 this)->FirstPageThat(TCondPageFunc(matchPageIndex), counts);
  1761.   CHECK(page == PageAtTabIndex(index));
  1762.   return page;
  1763. }
  1764.  
  1765. //
  1766. // Internal callback used to find the index of a particular page
  1767. // within a property sheet.
  1768. //
  1769. static bool
  1770. matchPagePtr(TPropertyPage* pPage, TPageInfo* pageInfo)
  1771. {
  1772.   if (pPage == pageInfo->Page)
  1773.     return true;
  1774.   else {
  1775.     pageInfo->Index++;
  1776.     return false;
  1777.   }
  1778. }
  1779.  
  1780. //
  1781. // Returns the index of the specified page or '-1' if the
  1782. // specified page could not be found in the child list of
  1783. // the sheet object
  1784. //
  1785. int
  1786. TPropertySheet::IndexOfPage(const TPropertyPage* page) const
  1787. {
  1788.   PRECONDITION(page);
  1789.   TPageInfo pi = {page, 0};
  1790.   int index = CONST_CAST(TPropertySheet*,
  1791.      this)->FirstPageThat(TCondPageFunc(matchPagePtr), &pi) ? pi.Index : -1;
  1792.   CHECK(index == TabIndexOfPage(page));
  1793.   return index;
  1794. }
  1795.  
  1796.  
  1797.  
  1798. DEFINE_RESPONSE_TABLE1(TPropertySheet, TWindow)
  1799.   // Messages handled by Sheet
  1800.   //
  1801.   EV_WM_ACTIVATE,
  1802.   EV_WM_SETFOCUS,
  1803.   EV_WM_SIZE,
  1804.   EV_WM_GETFONT,
  1805.  
  1806.   // Tab Control Notifications
  1807.   //
  1808.   EV_TCN_SELCHANGE(ID_TabControl, TabSelChange),
  1809.   EV_TCN_SELCHANGING(ID_TabControl, TabSelChanging),
  1810.   EV_TCN_KEYDOWN(ID_TabControl, TabKeyDown),
  1811.  
  1812.   // Button notifications
  1813.   //
  1814.   EV_COMMAND_AND_ID(ID_APPLY,  ButtonClicked),
  1815.   EV_COMMAND_AND_ID(ID_BACK,   ButtonClicked),
  1816.   EV_COMMAND_AND_ID(ID_NEXT,   ButtonClicked),
  1817.   EV_COMMAND_AND_ID(ID_FINISH, ButtonClicked),
  1818.   EV_COMMAND_AND_ID(IDOK,      ButtonClicked),
  1819.   EV_COMMAND_AND_ID(IDCANCEL,  ButtonClicked),
  1820.   EV_COMMAND_AND_ID(IDHELP,    ButtonClicked),
  1821. END_RESPONSE_TABLE;
  1822.  
  1823. //
  1824. // Returns a unique name under which the PropertSheet will be
  1825. // registered..
  1826. //
  1827. char*
  1828. TPropertySheet::GetClassName()
  1829. {
  1830.   // When encapsulating the Common Control's implementation we're
  1831.   // unaware of and not concerned with the window's classname
  1832.   // since the Common Control DLL exposes a high level DLL to
  1833.   // create the interface...
  1834.   //
  1835.   return "OWL_PropertySheet";
  1836. }
  1837.  
  1838. //
  1839. // Updates the Window CLASS attributes of the PropertySheet.
  1840. //
  1841. void
  1842. TPropertySheet::GetWindowClass(WNDCLASS& wndClass)
  1843. {
  1844.   TWindow::GetWindowClass(wndClass);
  1845.   wndClass.hbrBackground = HBRUSH(COLOR_BTNFACE + 1);
  1846. }
  1847.  
  1848. //
  1849. // WM_SIZE Handler
  1850. //
  1851. void
  1852. TPropertySheet::EvSize(uint sizeType, TSize& size)
  1853. {
  1854.   TWindow::EvSize(sizeType, size);
  1855.   if (!UseNative) {
  1856.     PRECONDITION(Tab && Tab->GetHandle());
  1857.  
  1858.     // Resize tab control
  1859.     //
  1860.   }
  1861. }
  1862.  
  1863. //
  1864. // WM_ACTIVATE Handler
  1865. //
  1866. void
  1867. TPropertySheet::EvActivate(uint active, bool minimized, THandle other)
  1868. {
  1869.   if (!UseNative) {
  1870.     // Store the handle of the focus window when we
  1871.     // are deactivated...
  1872.     //
  1873.     if (active == WA_INACTIVE)
  1874.       FocusHwnd = GetFocus();
  1875.   }
  1876.   TWindow::EvActivate(active, minimized, other);
  1877. }
  1878.  
  1879. //
  1880. // WM_SETFOCUS Handler
  1881. //
  1882. void
  1883. TPropertySheet::EvSetFocus(HWND hWndLostFocus)
  1884. {
  1885.   if (!UseNative) {
  1886.     // Restore focus to the saved 'Focus' window
  1887.     //
  1888.     if (FocusHwnd && ::IsWindow(FocusHwnd))
  1889.       ::SetFocus(FocusHwnd);
  1890.   }
  1891.   TWindow::EvSetFocus(hWndLostFocus);
  1892. }
  1893.  
  1894. //
  1895. // WM_GETFONT Handler
  1896. //
  1897. HFONT
  1898. TPropertySheet::EvGetFont()
  1899. {
  1900.   if (!UseNative) {
  1901.     if (Font) {
  1902.       return *Font;
  1903.     }
  1904.   }
  1905.   return HFONT(DefaultProcessing());
  1906. }
  1907.  
  1908. //
  1909. // TCN_SELCHANGE Handler
  1910. //
  1911. void
  1912. TPropertySheet::TabSelChange(TNotify&)
  1913. {
  1914.   if (!UseNative) {
  1915.     int index = Tab->GetSel();
  1916.     SetActivePage(index);
  1917.   }
  1918.   else {
  1919.     DefaultProcessing();
  1920.   }
  1921. }
  1922.  
  1923. //
  1924. // TCN_SELCHANGING Handler
  1925. //
  1926. bool
  1927. TPropertySheet::TabSelChanging(TNotify&)
  1928. {
  1929.   if (!UseNative) {
  1930.     // Do not veto any tab changes
  1931.     //
  1932.     return false;
  1933.   }
  1934.   else {
  1935.     return DefaultProcessing() != 0;
  1936.   }
  1937. }
  1938.  
  1939. //
  1940. // TCN_KEYDOWN Handler
  1941. //
  1942. void
  1943. TPropertySheet::TabKeyDown(TTabKeyDown&)
  1944. {
  1945.   if (!UseNative) {
  1946.     //
  1947.     // NOP
  1948.   }
  1949.   else {
  1950.     DefaultProcessing();
  1951.   }
  1952. }
  1953.  
  1954. //
  1955. // WM_COMMAND, code==0 (BN_CLICKED) handler
  1956. //
  1957. void            
  1958. TPropertySheet::ButtonClicked(uint id) 
  1959. {
  1960.   if (UseNative)
  1961.     DefaultProcessing();
  1962.   else {
  1963.     switch (id) {
  1964.       case  IDOK:
  1965.             Apply();
  1966.             CloseWindow(id);
  1967.             break;
  1968.  
  1969.       case  IDCANCEL:
  1970.             Destroy(id);
  1971.             break;
  1972.  
  1973.       case  ID_APPLY:
  1974.             Apply();
  1975.             break;
  1976.  
  1977.       case  IDHELP:
  1978.             TPropertyPage *activePg = GetActivePage();
  1979.             if (activePg != NULL) {
  1980.               TPshNotify info(*this, 0, PSN_HELP, 0);
  1981.               activePg->SendNotification(PropPageID, info);
  1982.             }
  1983.             break;
  1984.  
  1985.     }
  1986.   }
  1987. }
  1988.  
  1989. //
  1990. //
  1991. //
  1992. HWND
  1993. TPropertySheet::CreateButton(int id, const char* caption)
  1994. {
  1995.   return ::CreateWindow("BUTTON", caption, BS_PUSHBUTTON|WS_CHILD|WS_GROUP|
  1996.                         WS_TABSTOP|WS_VISIBLE, 0, 0, 0, 0, *this, HMENU(id),
  1997.                         *GetModule(), 0);
  1998. }
  1999.  
  2000. //
  2001. // Position the buttons of the property sheet
  2002. //
  2003. bool
  2004. TPropertySheet::CreateButtons()
  2005. {
  2006.   char rc[_MAX_PATH];
  2007.   int size = defBtnSz;
  2008.   TSheetBtnInfo* info = defBtn;
  2009.   for (int i=0; i<size; i++) {
  2010.     char* p = GetModule()->LoadString(info[i].strId, rc, sizeof(rc)) ? 
  2011.                                       rc : info[i].defStr;
  2012.  
  2013.     // Special case PSH_NOAPPLYNOW
  2014.     //
  2015.     if (info[i].id == int(ID_APPLY) && 
  2016.        (HeaderInfo.dwFlags & PSH_NOAPPLYNOW))
  2017.       continue;
  2018.  
  2019.     // Create button and initialize its attributes
  2020.     //
  2021.     HWND btn = CreateButton(defBtn[i].id, p);
  2022.     if (!btn)
  2023.       return false;
  2024.     if (Font)
  2025.       ::SendMessage(btn, WM_SETFONT, WPARAM(HFONT(*Font)), TRUE);  
  2026.   }
  2027.  
  2028.   // If user requested HASHELP style, create a Help button.
  2029.   //
  2030.   if (HeaderInfo.dwFlags & PSH_HASHELP) {
  2031.     char* p = GetModule()->LoadString(hlpBtn.strId, rc, sizeof(rc))
  2032.       ? rc : hlpBtn.defStr;
  2033.     HWND btn = CreateButton(hlpBtn.id, p);
  2034.     if (!btn)
  2035.       return false;
  2036.     if (Font)
  2037.       ::SendMessage(btn, WM_SETFONT, WPARAM(HFONT(*Font)), TRUE);
  2038.   }
  2039.  
  2040.   return true;
  2041. }
  2042.  
  2043. //
  2044. //
  2045. bool
  2046. TPropertySheet::UpdateButtons()
  2047. {
  2048.   TRect rect;
  2049.   GetClientRect(rect);
  2050.   int x = rect.right  - (Margin+ButtonSize.cx);
  2051.   int y = rect.bottom - (Margin+ButtonSize.cy);
  2052.   HWND btn;
  2053.   
  2054.   // If there is a Help button, position it.
  2055.   //
  2056.   if (HeaderInfo.dwFlags & PSH_HASHELP) {
  2057.     btn = GetDlgItem(hlpBtn.id);
  2058.     if (btn) {
  2059.       ::MoveWindow(btn, x, y, ButtonSize.cx, ButtonSize.cy, TRUE);
  2060.       x -= (Margin+ButtonSize.cx);
  2061.       ::EnableWindow(btn, 
  2062.                      (bool)(GetActivePage()->PageInfo.dwFlags & PSP_HASHELP));
  2063.     }
  2064.   }
  2065.   
  2066.   for (int i=0; i<sizeof(defBtn)/sizeof(defBtn[0]); i++) {
  2067.     btn = GetDlgItem(defBtn[i].id);
  2068.     if (btn) {
  2069.       ::MoveWindow(btn, x, y, ButtonSize.cx, ButtonSize.cy, TRUE);
  2070.       x -= (Margin+ButtonSize.cx);
  2071.     }
  2072.   }     
  2073.  
  2074.   
  2075.   btn = GetDlgItem(ID_APPLY);
  2076.   if (btn)
  2077.     ::EnableWindow(btn, ApplyEnabled ? TRUE : FALSE);
  2078.  
  2079.   return true; 
  2080. }
  2081.  
  2082. //
  2083. // Returns the TProperPage* of the active page
  2084. //
  2085. TPropertyPage*
  2086. TPropertySheet::GetActivePage() const
  2087. {
  2088.   return (ActiveIndex < 0) ? 0 : PageAtIndex(ActiveIndex);
  2089. }
  2090.  
  2091. //
  2092. // Activates the page at the specified index
  2093. //
  2094. bool
  2095. TPropertySheet::SetActivePage(int index)
  2096. {
  2097.   PRECONDITION(index < GetPageCount());
  2098.   PRECONDITION(index < Tab->GetCount());
  2099.   PRECONDITION(GetHandle());
  2100.  
  2101.   // Retrieve pointer to page
  2102.   //
  2103.   TPropertyPage* page = PageAtIndex(index);
  2104.   CHECK(page);
  2105.  
  2106.   // Create the page/dialog if necessary
  2107.   //
  2108.   if (!page->GetHandle()) {
  2109.     if (!page->GetParentO())
  2110.       page->SetParent(this);
  2111.     page->Create();
  2112.   }
  2113.  
  2114.   // If it's the very first page, skip the positioning - we're in a
  2115.   // catch-22 [The sheet/tab can only be positioned after the first
  2116.   // page is created since we need a page to invoke MapDialogRect, and
  2117.   // the page can only be positioned after the sheet/tab has been
  2118.   // positioned.]
  2119.   //
  2120.   if (ActiveIndex == -1) {
  2121.     ActiveIndex = index;
  2122.     return true;
  2123.   }
  2124.  
  2125.   // Position page if it's not current active and hide current
  2126.   //
  2127.   TPropertyPage* curActive = GetActivePage();
  2128.   if (curActive != page) {
  2129.     PositionPage(page);
  2130.     if (curActive)
  2131.       curActive->ShowWindow(SW_HIDE);
  2132.   }
  2133.  
  2134.   // Select appropriate tab, if necessary
  2135.   //
  2136.   if (Tab->GetSel() != index)
  2137.     Tab->SetSel(index);
  2138.  
  2139.   // Update 'ActiveIndex'
  2140.   //
  2141.   ActiveIndex = index;
  2142.  
  2143.   // Enable or disable the Help button, if present, according the the
  2144.   // style flags of the newly selected page.
  2145.   //
  2146.   HWND btn = GetDlgItem(IDHELP);
  2147.   if (btn) {
  2148.     ::EnableWindow(btn, (bool)(page->PageInfo.dwFlags & PSP_HASHELP));
  2149.   }
  2150.  
  2151.   return true;
  2152. }
  2153.  
  2154. //
  2155. // Callback used to set the text of tab items to the captions of a page.
  2156. //
  2157. static void
  2158. addTabItems(TPropertyPage* page, TTabControl* tab)
  2159. {
  2160.   // Retrieve the caption of the page
  2161.   //
  2162.   char txt[MaxTabTextLen];
  2163.   page->GetPageCaption(txt, sizeof(txt));
  2164.  
  2165.   // Set the text and have the tab control store the pointer
  2166.   // to the page in the application-defined space allocated
  2167.   // to each tab entry...
  2168.   //
  2169.   tab->Add(TTabItem(txt, 0, TParam2(page)));
  2170. }
  2171.  
  2172. //
  2173. // Synchronize tabs labels with current pages of the sheet
  2174. //
  2175. void
  2176. TPropertySheet::SyncTabAndPages()
  2177. {
  2178.   // Clear every tab item
  2179.   //
  2180.   Tab->DeleteAll();
  2181.  
  2182.   // Add the dialog/page titles to the tabs
  2183.   //
  2184.   ForEachPage(TActionPageFunc(addTabItems), Tab);
  2185. }
  2186.  
  2187. //
  2188. // Computes size/spacing of buttons using the active dialog and predefined
  2189. // dialog base units measurements
  2190. //
  2191. void
  2192. TPropertySheet::ComputeMarginAndButton()
  2193. {
  2194.   PRECONDITION(GetActivePage());
  2195.  
  2196.   // Margin       == 5 dlg units
  2197.   // Button Size  == 50x15 dlg units
  2198.   //
  2199.   TRect rect(5, 0, 50, 15); 
  2200.   GetActivePage()->MapDialogRect(rect);
  2201.   ButtonSize.cx = rect.right;
  2202.   ButtonSize.cy = rect.bottom;
  2203.   Margin = rect.left;
  2204. }
  2205.  
  2206. //
  2207. // Update the sheet's size to accomodate a page of the specified size.
  2208. //
  2209. void
  2210. TPropertySheet::UpdateTabAndSheetSize(const TRect& pageRect)
  2211. {
  2212.   // Update the tab's size to that it contains the page within its client
  2213.   // area...
  2214.   //
  2215.   TRect rect = pageRect;
  2216.   Tab->AdjustRect(true, rect);
  2217.   rect.MoveTo(Margin, Margin);
  2218.   Tab->SetWindowPos(0, rect, SWP_NOZORDER|SWP_NOACTIVATE);
  2219.  
  2220.   // Now update the sheet so that it contains the tab within its client area...
  2221.   //
  2222.   AdjustWindowRectEx(rect, GetStyle(), false, GetExStyle());
  2223.  
  2224.   // Lengthen the sheet to leave room for button and widen for margins
  2225.   //
  2226.   rect.bottom += ButtonSize.cy + Margin*3;
  2227.   rect.right += Margin*2;
  2228.  
  2229.   // Move sheet in relation to parent/owner
  2230.   //
  2231.   if (GetParentO() && GetParentO()->GetHandle()) {
  2232.     TRect rc;
  2233.     GetParentO()->GetWindowRect(rc);
  2234.     rect.MoveTo(rc.left + TUIMetric::CyMenu, rc.top + TUIMetric::CyMenu);
  2235.     if (rect.left < 0)
  2236.       rect.MoveTo(-rect.left, rect.top);
  2237.     else if (rect.right > TUIMetric::CxScreen)
  2238.       rect.MoveTo(TUIMetric::CxScreen-rect.Width(), rect.top);
  2239.     if (rect.top < 0)
  2240.       rect.MoveTo(rect.left, -rect.top);
  2241.     else if (rect.bottom > TUIMetric::CyScreen)
  2242.       rect.MoveTo(rect.left, TUIMetric::CyScreen-rect.Height());
  2243.   }
  2244.  
  2245.   // Update the sheet's size to accomodate
  2246.   //
  2247.   SetWindowPos(0, rect, SWP_NOZORDER|SWP_NOACTIVATE);
  2248. }
  2249.  
  2250. //
  2251. // Update a page's location within the sheet
  2252. //
  2253. void
  2254. TPropertySheet::PositionPage(TPropertyPage* page)
  2255. {
  2256.   // Retrieve area of tab (in terms of sheet window)
  2257.   //
  2258.   TRect rect;
  2259.   Tab->GetWindowRect(rect);
  2260.   ::MapWindowPoints(HWND_DESKTOP, *this, LPPOINT(&rect), 2);
  2261.  
  2262.   // Retrieve size of page
  2263.   //
  2264.   TRect pgRect;
  2265.   page->GetWindowRect(pgRect);
  2266.  
  2267.   // Request the tab for it's client area's rectangle
  2268.   //
  2269.   Tab->AdjustRect(false, rect);
  2270.  
  2271.   // Center the page
  2272.   //
  2273.   if (pgRect.Width() < rect.Width())
  2274.     rect.left += (rect.Width() - pgRect.Width())/2;
  2275.   if (pgRect.Height() < rect.Height())
  2276.     rect.top  += (rect.Height()- pgRect.Height())/2;
  2277.   page->SetWindowPos(HWND_TOP, rect, SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE);
  2278. }
  2279.  
  2280. //
  2281. // Returns the index of the tab item representing the specified page. Returns 
  2282. // -1 if the page is not currently represented by a tab item.
  2283. //
  2284. int
  2285. TPropertySheet::TabIndexOfPage(const TPropertyPage* page) const
  2286. {
  2287.   PRECONDITION(page);
  2288.   PRECONDITION(Tab && Tab->GetHandle());
  2289.  
  2290.   int i = Tab->GetCount();
  2291.   while(i) {
  2292.     TTabItem item(TCIF_PARAM);
  2293.     Tab->GetItem(--i, item);
  2294.     if (item.lParam == LPARAM(page))
  2295.       return i;
  2296.   }
  2297.   return -1;
  2298. }
  2299.  
  2300. //
  2301. // Returns the TPropertyPage pointer represented by the tab item at the 
  2302. // specified 'index'. Returns '0' if there are no tab items at the specified 
  2303. // index.
  2304. //
  2305. TPropertyPage*
  2306. TPropertySheet::PageAtTabIndex(int index) const
  2307. {
  2308.   PRECONDITION(index >= 0);
  2309.   PRECONDITION(Tab && Tab->GetHandle());
  2310.  
  2311.   TTabItem item(TCIF_PARAM);
  2312.   if (Tab->GetItem(index, item)) {
  2313.     return (TPropertyPage*)item.lParam;
  2314.   }
  2315.   return 0;
  2316. }
  2317. #endif
  2318.